﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD.Core {

    /// <summary>
    /// 静的メッシュ・コンポーネント
    /// </summary>
    /// <remarks>
    /// <see cref="Mesh"/>クラスはノードにアタッチ可能なコンポーネントの1つで､
    /// ノードに静的なメッシュを追加します。
    /// メッシュは(1)頂点データ(<see cref="VertexBuffer"/>)と(2)頂点と頂点を結ぶインデックス(<see cref="IndexBuffer"/>),
    /// それにポリゴンを塗りつぶす方法を定義する(3)アピアランス(<see cref="Appearance"/>)の3要素からなります。
    /// この3つのトリオを1つの単位としてサブメッシュと呼びます。メッシュは複数のサブメッシュで構成されます。
    /// 0番目のサブメッシュには上記の3つが必須です。1番目以降のサブメッシュは頂点バッファーとアピアランスは省略可能です。
    /// 省略された場合0番目のサブメッシュのそれが使用されます。
    /// 1番目以降のインデックスバッファーを省略した場合は単にそのサブメッシュは無視されます。
    /// <see cref="Mesh"/>コンポーネントは静的な変形しないメッシュを表し、
    /// その他の変形する派生型メッシュの基底となります。
    /// </remarks>
    public class Mesh : Component {

        #region Field
        /// <summary>
        /// 頂点バッファー
        /// </summary>
        protected VertexBuffer[] vbufs;

        /// <summary>
        /// インデックスバッファー
        /// </summary>
        protected IndexBuffer[] ibufs;

        /// <summary>
        /// アピアランス
        /// </summary>
        protected Appearance[] apps;
        #endregion

        #region Constructor
        /// <summary>
        /// サブメッシュ１つのコンストラクター
        /// </summary>
        public Mesh ()
            : this (1) {
        }

        /// <summary>
        /// 複数のサブメッシュからなるコンストラクター
        /// </summary>
        /// <param name="numSubmeshes">サブメッシュの数</param>
        public Mesh (int numSubmeshes) {
            if (numSubmeshes <= 0) {
                throw new ArgumentException ("Number of submesh is invalid");
            }
            this.vbufs = new VertexBuffer[numSubmeshes];
            this.ibufs = new IndexBuffer[numSubmeshes];
            this.apps = new Appearance[numSubmeshes];
        }

        #endregion

        #region Property

        /// <summary>
        /// サブメッシュの数
        /// </summary>
        /// <remarks>
        /// このメッシュを構成するサブメッシュの数です。
        /// </remarks>
        public int SubmeshCount {
            get { return ibufs.Length; }
        }

        /// <summary>
        /// 頂点バッファーを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 列挙子の中にnullが含まれる事はありません。
        /// </remarks>
        public IEnumerable<VertexBuffer> VertexBuffers {
            get { return vbufs.Where (x => x != null); }
        }

        /// <summary>
        /// インデックスバッファーを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 列挙子の中にnullが含まれる事はありません。
        /// </remarks>
        public IEnumerable<IndexBuffer> IndexBuffers {
            get { return ibufs.Where (x => x != null); }
        }

        /// <summary>
        /// アピアランスを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 列挙子の中にnullが含まれる事はありません。
        /// </remarks>
        public IEnumerable<Appearance> Appearances {
            get { return apps.Where (x => x != null); }
        }

        /// <summary>
        /// 有効なサブメッシュをすべて列挙する列挙子
        /// </summary>
        /// <remarks>
        /// サブメッシュは(1)頂点バッファー(2)インデックスバッファー(3)アピアランスの3つからなります｡
        /// 0番目のサブメッシュは3つ全てが必要ですが､1番目以降は頂点バッファーとアピアランスは省略可能です｡
        /// その場合は0番目のものが使用されます｡
        /// </remarks>
        public IEnumerable<Submesh> Submeshes {
            get {
                var list = new List<Submesh> ();
                for (var i = 0; i < SubmeshCount; i++) {
                    if (ibufs[i] != null) {
                        list.Add (new Submesh ((vbufs[i] == null) ? vbufs[0] : vbufs[i],
                                             ibufs[i],
                                             (apps[i] == null) ? apps[0] : apps[i]));
                    }
                }
                return list;
            }
        }

        /// <summary>
        /// 0番目のサブメッシュの頂点バッファー
        /// </summary>
        /// <remarks>
        /// </remarks>
        public VertexBuffer VertexBuffer {
            get { return vbufs[0]; }
        }

        /// <summary>
        /// 0番目のサブメッシュのインデックスバッファー
        /// </summary>
        /// <remarks>
        /// </remarks>
        public IndexBuffer IndexBuffer {
            get { return ibufs[0]; }
        }

        /// <summary>
        /// 0番目のサブメッシュのアピアランス
        /// </summary>
        /// <remarks>
        /// </remarks>
        public Appearance Appearance {
            get { return apps[0]; }
        }

        /// <summary>
        /// このオブジェクトが参照する全DDDオブジェクトを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 参照にnullは含まれません。同一のオブジェクトが複数出現する可能性があります。
        /// </remarks>
        public override IEnumerable<Object> References {
            get {
                var objs = base.References.ToList ();
                objs.AddRange (vbufs);
                objs.AddRange (ibufs);
                objs.AddRange (apps);
                objs.RemoveAll (x => x == null);
                return objs;
            }
        }

        #endregion



        /// <summary>
        /// i番目の頂点バッファーを取得します
        /// </summary>
        /// <param name="index">サブメッシュ番号</param>
        /// <returns>頂点バッファー</returns>
        public VertexBuffer GetVertexBuffer (int index) {
            if (index < 0 || index >= SubmeshCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }
            return vbufs[index];
        }

        /// <summary>
        /// i番目のインデックスバッファーを取得します
        /// </summary>
        /// <param name="index">サブメッシュ番号</param>
        /// <returns>インデックスバッファー</returns>
        public IndexBuffer GetIndexBuffer (int index) {
            if (index < 0 || index >= SubmeshCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }
            return ibufs[index];
        }

        /// <summary>
        /// i番目のアピアランスを取得します
        /// </summary>
        /// <param name="index">サブメッシュ番号</param>
        /// <returns>アピアランス</returns>
        public Appearance GetAppearance (int index) {
            if (index < 0 || index >= SubmeshCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }
            return apps[index];
        }


        #region Method
        /// <summary>
        /// 0番目のサブメッシュに頂点バッファーをセットします。
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="vbuf">頂点バッファー</param>
        public void SetVertexBuffer (VertexBuffer vbuf) {
            this.vbufs[0] = vbuf;
        }

        /// <summary>
        /// 0番目のサブメッシュにインデックスバッファーをセットします
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="ibuf">インデックスバッファー</param>
        public void SetIndexBuffer (IndexBuffer ibuf) {
            this.ibufs[0] = ibuf;
        }


        /// <summary>
        /// 0番目のサブメッシュにアピアランスをセットします
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="app">アピアランス</param>
        public void SetAppearance (Appearance app) {
            this.apps[0] = app;
        }

        /// <summary>
        /// i番目の頂点バッファーをセットします
        /// </summary>
        /// <param name="index">サブメッシュ番号</param>
        /// <param name="vbuf">頂点バッファー</param>
        public void SetVertexBuffer (int index, VertexBuffer vbuf) {
            if (index < 0 || index >= SubmeshCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }
            this.vbufs[index] = vbuf;
        }

        /// <summary>
        /// i番目のインデクスバッファーをセットします
        /// </summary>
        /// <param name="index">サブメッシュ番号</param>
        /// <param name="ibuf">インデックスバッファー</param>
        public void SetIndexBuffer (int index, IndexBuffer ibuf) {
            if (index < 0 || index >= SubmeshCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }
            this.ibufs[index] = ibuf;
        }

        /// <summary>
        /// i番目のアピアランスをセットします
        /// </summary>
        /// <param name="index">サブメッシュ番号</param>
        /// <param name="app">アピアランス</param>
        public void SetAppearance (int index, Appearance app) {
            if (index < 0 || index >= SubmeshCount) {
                throw new IndexOutOfRangeException ("Index is out of range");
            }
            this.apps[index] = app;
        }

        /// <summary>
        /// 現在のオブジェクトの状態を表す文字列を返します。
        /// </summary> 
        public override string ToString () {
            return string.Format ("Mesh: Submesh={0}", SubmeshCount);
        }
        #endregion


    }
}
